//*************************************************************************************
// File:     HiResTimer.cs
//*************************************************************************************
// Description: Encapsula un temporizador de alta resolucion.
//*************************************************************************************
// Classes:      HiResTimer
//*************************************************************************************
// Author:      ManOguaR
//*************************************************************************************

using System;
using Xeralabs.Common.Platform;

namespace Xeralabs.Timers
{
    /// <summary>
    /// Representa un temporizador de alta resolucion.
    /// </summary>
    public class HiResTimer
    {
        #region Locals
        protected bool isUsingQPF;
        protected bool isTimerStopped;
        protected long ticksPerSecond;
        protected long stopTime;
        protected long lastElapsedTime;
        protected long baseTime;
        #endregion

        #region Propiedad
        /// <summary>
        /// Obtiene True si el temporizador esta detenido.
        /// </summary>
        public bool IsStopped { get { return isTimerStopped; } }
        #endregion

        /// <summary>
        /// Constructor.
        /// </summary>
        public HiResTimer()
        {
            isTimerStopped = true;
            ticksPerSecond = 0;
            stopTime = 0;
            lastElapsedTime = 0;
            baseTime = 0;
            //Usar QueryPerformanceFrequency para obtener la frecuencia del temporizador
#if x64
            isUsingQPF = Win64DllImporter.QueryPerformanceFrequency(ref _ticksPerSecond);
#else
            isUsingQPF = Win32DllImporter.QueryPerformanceFrequency(ref ticksPerSecond);
#endif
        }

        /// <summary>
        /// Metodo para resetear el temporizador a 0 sin alterar si estado actual.
        /// </summary>
        public void Reset()
        {
            if (!isUsingQPF) return; //Nada que hacer

            //Obtener el tiempo actual o el de parada
            long time = 0;

            if (stopTime != 0) time = stopTime;
            else
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif

            baseTime = time;
            lastElapsedTime = time;
            stopTime = 0;
            isTimerStopped = false;
        }
        /// <summary>
        /// Inicia el temporizador.
        /// </summary>
        public void Start()
        {
            if (!isUsingQPF) return; //Nada que hacer

            //Obtener el tiempo actual o el de parada
            long time = 0;

            if (stopTime != 0) time = stopTime;
            else 
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif


            if (isTimerStopped) baseTime += (time - stopTime);
            stopTime = 0;
            lastElapsedTime = time;
            isTimerStopped = false;
        }
        /// <summary>
        /// Metodo para detener (or pausar) el temporizador.
        /// </summary>
        public void Stop()
        {
            if (!isUsingQPF)
                return; //Nada que hacer

            if (!isTimerStopped)
            {
                //Obtener el tiempo actual o el de parada
                long time = 0;
                if (stopTime != 0)
                    time = stopTime;
                else
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif

                stopTime = time;
                lastElapsedTime = time;
                isTimerStopped = true;
            }
        }

        /// <summary>
        /// Metodo para avanzar el timer 1/10 de segundo.
        /// </summary>
        public void Advance()
        {
            if (!isUsingQPF) return; //Nada que hacer

            stopTime += ticksPerSecond / 10;
        }

        /// <summary>
        /// Metodo para obtener el tiempo absoluto desde que se inicio el sistema, en milisegundos.
        /// </summary>
        /// <returns>
        /// El tiempo transcurrido desde que se inicio el temporizador, en milisegundos.
        /// </returns>
        public double GetAbsoluteTime()
        {
            if (!isUsingQPF) return -1.0; //Nada que hacer

            //Obtener el tiempo actual o el de parada
            long time = 0;

            if (stopTime != 0) time = stopTime;
            else 
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif

            double absolueTime = time / (double)ticksPerSecond;
            return absolueTime * 1000.0; //en milisegundos.
        }
        /// <summary>
        /// Metodo para obtener el tiempo actual modificado segun paradas y avances, en milisegundos.
        /// </summary>
        /// <returns>
        /// El tiempo actual del temporizador, en milisegundos.
        /// </returns>
        public double GetTime()
        {
            if (!isUsingQPF) return -1.0; //Nada que hacer

            //Obtener el tiempo actual o el de parada
            long time = 0;
            if (stopTime != 0) time = stopTime;
            else 
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif

            double appTime = (double)(time - baseTime) / (double)ticksPerSecond;
            return appTime * 1000.0; //en milisegundos.
        }
        /// <summary>
        /// Metodo para obtener el tiempo transcurrido entre dos llamadas GetElapsedTime(), modificado segun paradas y
        /// avances, en milisegundos.
        /// </summary>
        /// <returns>
        /// El tiempo transcurrido desde la ultima llamada GetElapsedTime(), en milisegundos.
        /// </returns>
        public double GetElapsedTime()
        {
            if (!isUsingQPF) return -1.0; //Nada que hacer

            //Obtener el tiempo actual o el de parada
            long time = 0;
            if (stopTime != 0) time = stopTime;
            else
#if x64
                Win64DllImporter.QueryPerformanceCounter(ref time);
#else
                Win32DllImporter.QueryPerformanceCounter(ref time);
#endif

            double elapsedTime = (double)(time - lastElapsedTime) / (double)ticksPerSecond;
            lastElapsedTime = time;

            return elapsedTime * 1000.0; //en milisegundos.
        }
    }
}
